package com._1.message.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.Set;

@Service
public class ThumbOrCancelService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void thumbOrCancel(String key, String userId, Integer score) {
        // 获取当前key的打分记录
        Double existScore = redisTemplate.opsForZSet().score(key, userId);
        boolean flag = existScore != null && existScore > 0;

        if (flag) {
            // 用户已打分，无需重复操作
            System.out.println("User already scored.");
        } else {
            // 用户未打分，添加到有序集合中
            redisTemplate.opsForZSet().add(key, userId, score);
            System.out.println("User scored successfully.");
        }
    }

    /**
     * 分布式秒杀点赞功能
     *
     * @param key      Redis键
     * @param userId   用户ID
     * @param score    点赞分数
     * @param maxCount 最大点赞人数
     * @return true 表示点赞成功，false 表示点赞失败
     */
    public boolean distributedThumbOrCancel(String key, String userId, Integer score, int maxCount) {
        String luaScript = "local userScore = redis.call('ZSCORE', KEYS[1], ARGV[1]) " +
                           "if userScore then " +
                           "   return 0 " +
                           "else " +
                           "   local count = redis.call('ZCARD', KEYS[1]) " +
                           "   if count < tonumber(ARGV[2]) then " +
                           "       redis.call('ZADD', KEYS[1], ARGV[3], ARGV[1]) " +
                           "       return 1 " +
                           "   else " +
                           "       return 0 " +
                           "   end " +
                           "end";

        // 将 Lua 脚本转换为字节数组
        byte[] scriptBytes = luaScript.getBytes(java.nio.charset.StandardCharsets.UTF_8);

        // 使用 eval 方法的字节数组版本执行 Lua 脚本
        Long result = (Long) redisTemplate.execute((RedisCallback<Object>) redisScript -> {
            return redisScript.eval(scriptBytes, ReturnType.INTEGER, 1, key.getBytes(), userId.getBytes(), String.valueOf(maxCount).getBytes(), String.valueOf(score).getBytes());
        });

        return result == 1;
    }

    /**
     * 查询指定key的打分记录，并按score倒序返回所有信息（包括评分）
     *
     * @param key   Redis键
     * @param start 起始索引（包含）
     * @param end   结束索引（包含）
     * @return 按score倒序的用户列表，包含评分信息
     */
    public Set<ScoredValue> getTopScores(String key, long start, long end) {
        // 使用reverseRangeByScoreWithScores方法按score倒序查询，并获取评分信息
        Set<ZSetOperations.TypedTuple<String>> scoredSet = redisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, start, end);

        // 将结果封装为Set<ScoredValue>
        Set<ScoredValue> result = new java.util.HashSet<>();
        for (ZSetOperations.TypedTuple<String> tuple : scoredSet) {
            result.add(new ScoredValue(tuple.getValue(), tuple.getScore()));
        }
        return result;
    }

    public Set<String> getTopKey(String key, long start, long end) {
        // 使用reverseRangeByScore方法按score倒序查询
        return redisTemplate.opsForZSet().reverseRangeByScore(key, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, start, end);
    }

    /**
     * 获取两个用户的共同关注列表
     *
     * @param userId1 第一个用户ID
     * @param userId2 第二个用户ID
     * @return 共同关注的用户列表
     */
    public Set<String> getCommonFollowings(String userId1, String userId2) {
        // 构造两个用户的关注列表键名
        String key1 = "user:" + userId1 + ":followings";
        String key2 = "user:" + userId2 + ":followings";
        return redisTemplate.opsForSet().intersect(key1, key2);
    }

    // 自定义类，用于存储用户ID和评分信息
    public static class ScoredValue {
        private final String userId;
        private final Double score;

        public ScoredValue(String userId, Double score) {
            this.userId = userId;
            this.score = score;
        }

        public String getUserId() {
            return userId;
        }

        public Double getScore() {
            return score;
        }

        @Override
        public String toString() {
            return "ScoredValue{" +
                    "userId='" + userId + '\'' +
                    ", score=" + score +
                    '}';
        }
    }
}